//-------------------------------------------------------------------------------------
// <copyright file="Orchestration.cs" company="Microsoft">
//      Copyright (c) 2003 Microsoft Corporation. All rights reserved.
//      THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
//      EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
//      WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// </copyright>
// <summary>
//      An orchestration.
// </summary>  
//-------------------------------------------------------------------------------------

namespace Microsoft.Sdc.Tasks.BizTalk2004.Configuration
{
	#region Using directives
    using System;
	using System.Collections;
	using System.Management;
	#endregion 

	/// <summary>
	/// An orchestration.
	/// </summary>
	internal class Orchestration : BizTalkConfigurableEntityBase
	{
		#region Member Variables

		private Host host;
		private BizTalkAssembly assembly;
        private OrchestrationPortCollection ports;
        private OrchestrationCollection invokedOrchestrations;

		#endregion 

		#region Constructors

		/// <summary>
		/// Creates an orchestration instance. This is private because instance of this class can only be created with
		/// the Load method
		/// </summary>
		/// <param name="assembly"> A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.BizTalkAssembly"/> object. </param>
		///<param name="name">The name of the orchestration.</param>
		private Orchestration(BizTalkAssembly assembly, string name) : base(assembly.Installation, name)
		{
		}

		#endregion

		#region Properties

		/// <summary>
		/// Gets the <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.BizTalkAssembly"/> to which this 
		/// orchestration belongs. 
		/// </summary>
		/// <value>
		/// The <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.BizTalkAssembly"/> object.
		/// </value>
        public BizTalkAssembly Assembly
        {
            get 
			{ 
				return this.assembly; 
			}
        }

		/// <summary>
		/// Gets the collection of send ports of this orchestration.
		/// </summary>
		/// <value>
		/// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.SendPortGroup"/> collection.
		/// </value>
        public OrchestrationPortCollection Ports
        {
            get 
			{ 
				if (this.ports == null)
				{
					this.ports = new OrchestrationPortCollection();
					BtsOrchestration actualOrchestration = Orchestration.GetActualOrchestration(this.assembly, this.Name, true);
					foreach (BtsOrchestrationPort actualPort in actualOrchestration.Ports)
					{
						OrchestrationPort configPort = OrchestrationPort.Load(this, actualPort.Name);
						this.ports.Add(configPort);
					}
				}
				return this.ports; 
			}
        }

		/// <summary>
		/// Gets the collection of orchestrations that are invoked from the current orchestration.
		/// </summary>
		/// <value>
		/// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.OrchestrationCollection"/> collection.
		/// </value>
        public OrchestrationCollection InvokedOrchestrations
        {
            get 
			{ 
				if (this.invokedOrchestrations == null)
				{
					this.invokedOrchestrations = new OrchestrationCollection();
					BtsOrchestration actualOrchestration = Orchestration.GetActualOrchestration(this.assembly, this.Name, true);
					foreach (BtsOrchestration invokedOrchestration in actualOrchestration.InvokedOrchestrations)
					{
						BizTalkAssembly containingAssembly = this.Installation.DeployedAssemblies[invokedOrchestration.BtsAssembly.Name];
						this.invokedOrchestrations.Add(containingAssembly.Orchestrations[invokedOrchestration.FullName]);
					}
				}
				return this.invokedOrchestrations; 
			}
        }

		/// <summary>
		/// Gets or sets the <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.Host"/> associated with the orchestration.
		/// </summary>
		/// <value>
		/// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.Host"/> object.
		/// </value>
        public Host Host
        {
            get 
			{ 
				return this.host; 
			}
			set 
			{
				this.host = value;
			}
        }

		#endregion 

		#region Methods

		/// <summary>
		/// Loads the details of an orchestration
		/// </summary>
		/// <param name="assembly">
		/// The <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.BizTalkAssembly"/> object that contains this 
		/// orchestration.
		/// </param>
		/// <param name="name">
		/// The name of the orchestration to load.
		/// </param>
		/// <returns>
		/// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.Orchestration"/> object. 
		/// </returns>
		/// <exception cref="System.InvalidOperationException">
		/// Thrown when the orchestration does not exist in the BizTalk Server installation.
		/// </exception>
		public static Orchestration Load(BizTalkAssembly assembly, string name)
		{
			//check inputs
			if (assembly == null)
			{
				throw new ArgumentException("assembly");
			}
			if (name == null || name.Length == 0) 
			{
				throw new ArgumentException("name");
			}

			//loading native types
			BtsOrchestration actualOrchestration = Orchestration.GetActualOrchestration(assembly, name, true);

			//create a config orchestration and set the basic properties
			Orchestration orchestration = new Orchestration(assembly, actualOrchestration.FullName);
			orchestration.assembly = assembly;
			if (actualOrchestration.Host != null)
			{
				orchestration.host = assembly.Installation.Hosts[actualOrchestration.Host.Name];
			}

			//return the orchestration
			return orchestration;
		}
        
		/// <summary>
		/// Gets the actual orchestration object that is deployed in BizTalk.
		/// </summary>
		/// <param name="assembly">
		/// The <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.BizTalkAssembly"/> object that contains this 
		/// orchestration.
		/// </param>
		/// <param name="name">
		/// The name of the orchestration to load.
		/// </param>
		/// <param name="throwIfNotFound">
		/// <b>true</b> to throw an error if the orchestration is not found, or <b>false</b> otherwise.
		/// </param>
		/// <returns>
		/// A BtsBtsOrchestration object. 
		/// </returns>
		/// <exception cref="System.InvalidOperationException">
		/// Thrown when <paramref name="throwIfNotFound"/> is <b>true</b> and the orchestration does not exist 
		/// in the BizTalk Server installation.
		/// </exception>
		private static BtsOrchestration GetActualOrchestration(BizTalkAssembly assembly, string name, bool throwIfNotFound)
		{
			BtsAssembly actualAssembly = assembly.Installation.CatalogExplorer.Assemblies[assembly.Name];
			BtsOrchestration actualOrchestration = actualAssembly.Orchestrations[name];
			if (throwIfNotFound && actualOrchestration == null)
			{
				throw new InvalidOperationException("The orchestration does not exist in the BizTalk Server installation.");
			}
			return actualOrchestration;
		}

		
	
		/// <summary>
		/// Enlists the orchestration.
		/// </summary>
		public void Enlist()
		{
			this.Installation.CatalogExplorer.Assemblies[this.Assembly.Name].Orchestrations[this.Name].Status = OrchestrationStatus.Enlisted;
			this.Installation.CatalogExplorer.SaveChanges();
		}

		/// <summary>
		/// Starts the orchestration.
		/// </summary>
		public void Start()
		{
			this.Installation.CatalogExplorer.Assemblies[this.Assembly.Name].Orchestrations[this.Name].Status = OrchestrationStatus.Started;
			this.Installation.CatalogExplorer.SaveChanges();
		}

		/// <summary>
		/// Stops the orchestration.
		/// </summary>
		public void Stop()
		{
			this.Installation.CatalogExplorer.Assemblies[this.Assembly.Name].Orchestrations[this.Name].Status = OrchestrationStatus.Unenlisted;
			this.Installation.CatalogExplorer.SaveChanges();
		}

		/// <summary>
		/// Unenlists the orchestration.
		/// </summary>
		public void UnEnlist()
		{
			this.UnEnlist(false);
		}

		/// <summary>
		/// Unenlists the orchestration.
		/// </summary>
		/// <param name="terminateExisting">True to terminate any existing instances</param>
		public void UnEnlist(bool terminateExisting)
		{
			BtsOrchestration orch = this.Installation.CatalogExplorer.Assemblies[this.Assembly.Name].Orchestrations[this.Name];
			orch.AutoTerminateInstances = terminateExisting;
			orch.Status = OrchestrationStatus.Unenlisted;
			this.Installation.CatalogExplorer.SaveChanges();
		}

		public void TerminateInstances()
		{
			//BtsOrchestration orch = this.Installation.CatalogExplorer.Assemblies[this.Assembly.Name].Orchestrations[this.Name];
			
			// This will terminate all of the Service Instances
			const uint SERVICE_CLASS_ORCHESTRATION = 1;

			string orchName = this.Name.Substring(this.Name.LastIndexOf(".") + 1);
			string WLQuery = string.Format(
				"SELECT * FROM MSBTS_ServiceInstance WHERE ServiceClass = {0} and ServiceStatus > 2 AND ServiceName = '{1}'", 
				SERVICE_CLASS_ORCHESTRATION,
				orchName);
			ManagementObjectSearcher searcherServiceInstance = new ManagementObjectSearcher(
				new ManagementScope ("root\\MicrosoftBizTalkServer"), 
				new WqlObjectQuery(WLQuery), 
				null);

			ArrayList serviceClassID = new ArrayList();
			ArrayList serviceTypeID = new ArrayList();
			ArrayList serviceInstanceID = new ArrayList();
			int numberServiceInstancesFound = searcherServiceInstance.Get().Count;

			// Can only terminate 2047 Instances at once.  Loop through all instances and terminate 2047 at a time
			if ( numberServiceInstancesFound > 0 )
			{
                string hostQueueFullPath;
                string[] instanceIdList; 
				string[] ClassIdList;
				string[] TypeIdList; 

				string hostName = string.Empty;
				int i = 0;

				foreach ( ManagementObject objServiceInstance in searcherServiceInstance.Get() )
				{
					// It is safe to assume that all service instances belong to a single Host.
					if (hostName == string.Empty)
					{
						hostName = objServiceInstance["HostName"].ToString();
					}

					if (i > 1999)
					{
						// Now have 2000 entries, so convert to an array
						ClassIdList = (string[])serviceClassID.ToArray(typeof(string));
						TypeIdList = (string[])serviceTypeID.ToArray(typeof(string));
						instanceIdList = (string[])serviceInstanceID.ToArray(typeof(string));
 
						hostQueueFullPath = string.Format("root\\MicrosoftBizTalkServer:MSBTS_HostQueue.HostName=\"{0}\"", hostName);
						ManagementObject objHostQueue = new ManagementObject(hostQueueFullPath);
 
						objHostQueue.InvokeMethod("TerminateServiceInstancesByID",
							new object[] {ClassIdList, TypeIdList, instanceIdList}
							);
						// reset the Arrays
						i = 0;
						serviceClassID.Clear();
						serviceTypeID.Clear();
						serviceInstanceID.Clear();
					}
                                    
					serviceClassID.Add(objServiceInstance["ServiceClassId"].ToString());
					serviceTypeID.Add(objServiceInstance["ServiceTypeId"].ToString());
					serviceInstanceID.Add(objServiceInstance["InstanceID"].ToString());
                   
					i++;
				}
				// if any of the arrays are still populated, need to terminate one last time.
				if (serviceClassID.Count > 0)
				{
					ClassIdList = (string[])serviceClassID.ToArray(typeof(string));
					TypeIdList = (string[])serviceTypeID.ToArray(typeof(string));
					instanceIdList = (string[])serviceInstanceID.ToArray(typeof(string));
                        
					hostQueueFullPath = string.Format("root\\MicrosoftBizTalkServer:MSBTS_HostQueue.HostName=\"{0}\"", hostName);
					ManagementObject objHostQueue = new ManagementObject(hostQueueFullPath);
 
					objHostQueue.InvokeMethod("TerminateServiceInstancesByID",
						new object[] {ClassIdList, TypeIdList, instanceIdList}
						);
				}
			}
		}

		/// <summary>
		/// Saves this Orchestration
		/// </summary>
		protected internal override void SaveImpl()
		{

			//saves the port bindings 
			BtsOrchestration actualOrch = Orchestration.GetActualOrchestration(this.Assembly, this.Name, false);
			if (this.host == null)
			{
				actualOrch.Host = null;
			}
			else
			{
				actualOrch.Host = this.Installation.CatalogExplorer.Hosts[this.host.Name];
			}

			if (this.Ports.Count == 0)
			{
				foreach (BtsOrchestrationPort p in actualOrch.Ports)
				{
					p.SendPort = null;
					p.ReceivePort = null;
					p.SendPortGroup = null;
				}
				//actualOrch.Ports.Clear();
			}
			else
			{
				foreach(OrchestrationPort port in this.Ports)
				{
					if (port.SendPort != null)
					{
						actualOrch.Ports[port.Name].SendPort =  this.Installation.CatalogExplorer.SendPorts[port.SendPort.Name];
					}
					if (port.SendPortGroup != null)
					{
						actualOrch.Ports[port.Name].SendPortGroup= this.Installation.CatalogExplorer.SendPortGroups[port.SendPortGroup.Name];
					}
					if (port.ReceivePort != null)
					{
						actualOrch.Ports[port.Name].ReceivePort = this.Installation.CatalogExplorer.ReceivePorts[port.ReceivePort.Name];
					}
				}
			}

			this.Installation.CatalogExplorer.SaveChanges();
		}




		/// <summary>
		/// Checks if an orchestration exists in an assembly.
		/// </summary>
		/// <param name="assembly">
		/// The <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.BizTalkAssembly"/> object that contains this 
		/// orchestration.
		/// </param>
		/// <param name="name">
		/// The name of the orchestration to load.
		/// </param>
		/// <returns>
		/// true if the orchestration exists, false otherwise. 
		/// </returns>
		public static bool Exists(BizTalkAssembly assembly, string name)
		{
			//loading native types
			BtsOrchestration actualOrchestration = Orchestration.GetActualOrchestration(assembly, name, false);
			return (actualOrchestration != null);
		}


		#endregion 
	}
}

